﻿using System;
using System.Collections.Generic;
#if DT_POLYREF64
using dtPolyRef = System.UInt64;
#else
using dtPolyRef = System.UInt32;
#endif
public static partial class Detour
{
    public const int DT_VERTS_PER_POLYGON = 6;

    public const int DT_NAVMESH_MAGIC = 'D' << 24 | 'N' << 16 | 'A' << 8 | 'V';

    public const int DT_NAVMESH_VERSION = 7;

    public const uint DT_NULL_LINK = 0xffffffff;
    public const ushort DT_EXT_LINK = 0x8000;
    public enum dtPolyTypes
    {
        /// The polygon is a standard convex polygon that is part of the surface of the mesh.
        DT_POLYTYPE_GROUND = 0,
        /// The polygon is an off-mesh connection consisting of two vertices.
        DT_POLYTYPE_OFFMESH_CONNECTION = 1,
    };
    public class dtNavMeshParams 
    {
      public float[] orig = new float[3];
      public float tileWidth;
      public float tileHeight;
      public int maxTiles;
      public int maxPolys;

      public dtNavMeshParams Clone() 
      {
          dtNavMeshParams copy = new dtNavMeshParams();
          for (int i = 0; i < orig.Length; ++i)
          {
              copy.orig[i] = orig[i];
          }
          copy.tileWidth = tileWidth;
          copy.tileHeight = tileHeight;
          copy.maxTiles = maxTiles;
          copy.maxPolys = maxPolys;
          return copy;
      }
    }
    public class dtLink 
    {
        public dtPolyRef polyRef;
        public uint next;
        public byte edge;
        public byte side;
        public byte bmin;
        public byte bmax;

        public byte[] ToBytes()
        {
            List<byte> bytes = new List<byte>();
            bytes.AddRange(BitConverter.GetBytes(polyRef));
            bytes.AddRange(BitConverter.GetBytes(next));
            bytes.Add(edge);
            bytes.Add(side);
            bytes.Add(bmin);
            bytes.Add(bmax);
            return bytes.ToArray();
        }
    }
    public class dtPoly 
    {
        public uint firstLink;
        public ushort[] verts = new ushort[DT_VERTS_PER_POLYGON];
        public ushort[] neis = new ushort[DT_VERTS_PER_POLYGON];

        public ushort flags;
        public byte vertCount;
        public byte areaAndtype;

        public byte[] ToBytes()
        {
            List<byte> bytes = new List<byte>();
            bytes.AddRange(BitConverter.GetBytes(firstLink));
            for (int i = 0; i < DT_VERTS_PER_POLYGON; ++i)
            {
                bytes.AddRange(BitConverter.GetBytes(verts[i]));
            }
            for (int i = 0; i < DT_VERTS_PER_POLYGON; ++i)
            {
                bytes.AddRange(BitConverter.GetBytes(neis[i]));
            }
            bytes.AddRange(BitConverter.GetBytes(flags));
            bytes.Add(vertCount);
            bytes.Add(areaAndtype);
            return bytes.ToArray();
        }

        public void setArea(byte a)
        {
            areaAndtype = (byte)(((int)areaAndtype & 0xc0) | ((int)a & 0x3f));
        }
        public void setType(byte t)
        {
            areaAndtype = (byte)(((int)areaAndtype & 0x3f) | (t << 6));
        }
        public byte getArea()
        {
            return (byte)((int)areaAndtype & 0x3f);
        }
        public byte getType()
        {
            return (byte)((int)areaAndtype >> 6);
        }
    }
    public class dtMeshHeader 
    {
        public int magic;  
        public int version;
        public int x;
        public int y;
        public int layer;
        public uint userId;
        public int polyCount;
        public int vertCount;
        public int maxLinkCount;
        public int detailMeshCount;

        public int detailVertCount;

        public int detailTriCount;
        public int bvNodeCount;
        public int offMeshConCount;
        public int offMeshBase;
        public float walkableHeight;
        public float walkableRadius;
        public float walkableClimb;
        public float[] bmin = new float[3];
        public float[] bmax = new float[3];

        public float bvQuantFactor;

        public byte[] ToBytes() 
        {
            List<byte> bytes = new List<byte>();
            bytes.AddRange(BitConverter.GetBytes(magic));
            bytes.AddRange(BitConverter.GetBytes(version));
            bytes.AddRange(BitConverter.GetBytes(x));
            bytes.AddRange(BitConverter.GetBytes(y));
            bytes.AddRange(BitConverter.GetBytes(layer));
            bytes.AddRange(BitConverter.GetBytes(userId));
            bytes.AddRange(BitConverter.GetBytes(polyCount));
            bytes.AddRange(BitConverter.GetBytes(vertCount));
            bytes.AddRange(BitConverter.GetBytes(maxLinkCount));
            bytes.AddRange(BitConverter.GetBytes(detailMeshCount));
            bytes.AddRange(BitConverter.GetBytes(detailVertCount));
            bytes.AddRange(BitConverter.GetBytes(detailTriCount));
            bytes.AddRange(BitConverter.GetBytes(bvNodeCount));
            bytes.AddRange(BitConverter.GetBytes(offMeshConCount));
            bytes.AddRange(BitConverter.GetBytes(offMeshBase));
            bytes.AddRange(BitConverter.GetBytes(walkableHeight));
            bytes.AddRange(BitConverter.GetBytes(walkableRadius));
            bytes.AddRange(BitConverter.GetBytes(walkableClimb));
            for (int i = 0; i < 3; ++i)
            {
                bytes.AddRange(BitConverter.GetBytes(bmin[i]));
            }
            for (int i = 0; i < 3; ++i)
            {
                bytes.AddRange(BitConverter.GetBytes(bmax[i]));
            }
            bytes.AddRange(BitConverter.GetBytes(bvQuantFactor));
            return bytes.ToArray();
        }
    }
    public class dtPolyDetail 
    {
        public uint vertBase;
        public uint triBase;
        public byte vertCount;
        public byte triCount;

        public byte[] ToBytes()
        {
            List<byte> bytes = new List<byte>();
            bytes.AddRange(BitConverter.GetBytes(vertBase));
            bytes.AddRange(BitConverter.GetBytes(triBase));
            bytes.Add(vertCount);
            bytes.Add(triCount);
            return bytes.ToArray();
        }
    }
    public class dtBVNode
    {
      public ushort[] bmin = new ushort[3];
      public ushort[] bmax = new ushort[3];
      public int i;
      public byte[] ToBytes()
      {
          List<byte> bytes = new List<byte>();
          for (int j = 0; j < bmin.Length; ++j)
          {
              bytes.AddRange(BitConverter.GetBytes(bmin[j]));
          }
          for (int j = 0; j < bmax.Length; ++j)
          {
              bytes.AddRange(BitConverter.GetBytes(bmax[j]));
          }
          bytes.AddRange(BitConverter.GetBytes(i));
          return bytes.ToArray();
      }
    }

    public class dtOffMeshConnection 
    {
        public float[] pos = new float[6];
        public float rad;
        public ushort poly;
        public byte flags;
        public byte side;
        public uint userId;
        public byte[] ToBytes()
        {
            List<byte> bytes = new List<byte>();
            for (int i = 0; i < 6; ++i)
            {
                bytes.AddRange(BitConverter.GetBytes(pos[i]));
            }
            bytes.AddRange(BitConverter.GetBytes(rad));
            bytes.AddRange(BitConverter.GetBytes(poly));
            bytes.Add(flags);
            bytes.Add(side);
            bytes.AddRange(BitConverter.GetBytes(userId));
            return bytes.ToArray();
        }
    }

    public class dtMeshTile 
    {
        public uint salt;
        public uint linksFreeList;
        public dtMeshHeader header;
        public dtPoly[] polys;
        public float[] verts;
        public dtLink[] links;
        public dtPolyDetail[] detailMeshes;

        public float[] detailVerts;
        public byte[] detailTris;

        public dtBVNode[] bvTree;

        public dtOffMeshConnection[] offMeshCons;
        public dtRawTileData data;
        public int dataSize;
        public int flags;
        public dtMeshTile next;
    }
    public class dtRawTileData 
    {
        public dtMeshHeader header;
        public dtPoly[] polys;
        public float[] verts;
        public dtLink[] links;
        public dtPolyDetail[] detailMeshes;

        public float[] detailVerts;
        public byte[] detailTris;

        public dtBVNode[] bvTree;

        public dtOffMeshConnection[] offMeshCons;

        public int flags;
        public byte[] ToBytes()
        {
            List<byte> bytes = new List<byte>();
            bytes.AddRange(header.ToBytes());
            for (int i = 0; i < polys.Length; ++i)
            {
                bytes.AddRange(polys[i].ToBytes());
            }
            for (int i = 0; i < verts.Length; ++i)
            {
                bytes.AddRange(BitConverter.GetBytes(verts[i]));
            }
            for (int i = 0; i < links.Length; ++i)
            {
                bytes.AddRange(links[i].ToBytes());
            }
            for (int i = 0; i < detailMeshes.Length; ++i)
            {
                bytes.AddRange(detailMeshes[i].ToBytes());
            }
            for (int i = 0; i < detailVerts.Length; ++i)
            {
                bytes.AddRange(BitConverter.GetBytes(detailVerts[i]));
            }
            for (int i = 0; i < detailTris.Length; ++i)
            {
                bytes.Add(detailTris[i]);
            }
            for (int i = 0; i < bvTree.Length; ++i)
            {
                bytes.AddRange(bvTree[i].ToBytes());
            }
            for (int i = 0; i < offMeshCons.Length; ++i)
            {
                bytes.AddRange(offMeshCons[i].ToBytes());
            }
            bytes.AddRange(BitConverter.GetBytes(flags));
            return bytes.ToArray();
        }
    }
}